//
// Created by breaktcn on 2017/8/2.
//

#include "my_transcoding.h"



void transcode(char *fileName){

    AVFormatContext *fileFormatCxt,*outFormatCxt;
    AVOutputFormat *outputFormat;
    AVStream *outAudioStream,*outVideoStream;
    AVInputFormat *fileInputFormat;
    AVCodec *audioEncodeAVcodec = NULL,*videoEncodeAVcodec = NULL;
    AVCodecContext *audioAVCodecCxt,*videoAVCodecCxt,*audioEncodeCodecCxt,*videoEncodeCodecCxt;
    int audio_index = 0,video_index = 0;
    AVPacket pkt,encodePkt;
    AVFrame *avFrame = NULL;
    int ret = 0;

    av_register_all();
    fileFormatCxt = avformat_alloc_context();
    avformat_open_input(&fileFormatCxt,fileName,NULL,NULL);
    avformat_find_stream_info(fileFormatCxt,NULL);

    //decode;
    audioAVCodecCxt = avcodec_alloc_context3(NULL);
    videoAVCodecCxt = avcodec_alloc_context3(NULL);
    for (int i = 0; i < fileFormatCxt->nb_streams; ++i) {
        if (fileFormatCxt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
            audio_index = i;
            AVCodecParameters *audioAVCodecParameter = fileFormatCxt->streams[audio_index]->codecpar;
            AVCodec *audioAVcodec = avcodec_find_decoder(audioAVCodecParameter->codec_id);
            avcodec_parameters_to_context(audioAVCodecCxt,audioAVCodecParameter);
            avcodec_open2(audioAVCodecCxt,audioAVcodec,NULL);
            continue;
        }
        if (fileFormatCxt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
            video_index = i;
            AVCodecParameters *videoAVCodecParameter = fileFormatCxt->streams[video_index]->codecpar;
            AVCodec *videoAVcodec = avcodec_find_decoder(videoAVCodecParameter->codec_id);
            avcodec_parameters_to_context(videoAVCodecCxt,videoAVCodecParameter);
            avcodec_open2(videoAVCodecCxt,videoAVcodec,NULL);
            videoAVCodecCxt->framerate = av_guess_frame_rate(fileFormatCxt,fileFormatCxt->streams[i],NULL);
            continue;
        }
    }
    //input information.
    av_dump_format(fileFormatCxt,0,fileName,0);

    //encode;
    audioEncodeAVcodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    audioEncodeCodecCxt = avcodec_alloc_context3(audioEncodeAVcodec);
    videoEncodeAVcodec = avcodec_find_encoder(AV_CODEC_ID_H264);
    videoEncodeCodecCxt = avcodec_alloc_context3(videoEncodeAVcodec);

//    audioEncodeCodecCxt->bit_rate = 44100; //47954
//    audioEncodeCodecCxt->time_base =(AVRational){1,44100};//1,44100
//    audioEncodeCodecCxt->framerate =(AVRational){0,1};//0,1
//    audioEncodeCodecCxt->sample_fmt = AV_SAMPLE_FMT_FLTP;
//    audioEncodeCodecCxt->sample_rate = 44100;//44100
//    audioEncodeCodecCxt->codec_type = AVMEDIA_TYPE_AUDIO;

    audioEncodeCodecCxt->sample_fmt = audioEncodeAVcodec->sample_fmts[0];
    audioEncodeCodecCxt->sample_rate = audioAVCodecCxt->sample_rate;//44100
    audioEncodeCodecCxt->bit_rate = audioAVCodecCxt->bit_rate; //47954
    audioEncodeCodecCxt->time_base =(AVRational){1,audioEncodeCodecCxt->sample_rate};//1,44100
    audioEncodeCodecCxt->framerate =audioAVCodecCxt->framerate;//0,1
    audioEncodeCodecCxt->channel_layout = audioAVCodecCxt->channel_layout;
    audioEncodeCodecCxt->channels = av_get_channel_layout_nb_channels(audioEncodeCodecCxt->channel_layout);
    audioEncodeCodecCxt->codec_type = AVMEDIA_TYPE_AUDIO;

    avcodec_open2(audioEncodeCodecCxt,audioEncodeAVcodec,NULL);

    videoEncodeCodecCxt->bit_rate = 400000;
    videoEncodeCodecCxt->width = 352;
    videoEncodeCodecCxt->height = 288;
    videoEncodeCodecCxt->framerate = (AVRational){20,1};
//    videoEncodeCodecCxt->time_base = (AVRational){1,25};
    videoEncodeCodecCxt->time_base = av_inv_q(videoEncodeCodecCxt->framerate);
//    videoEncodeCodecCxt->gop_size = 250;
//    videoEncodeCodecCxt->max_b_frames = 3;
    videoEncodeCodecCxt->pix_fmt = AV_PIX_FMT_YUV420P;
    videoEncodeCodecCxt->me_range = 16;
    videoEncodeCodecCxt->max_qdiff = 4;
    videoEncodeCodecCxt->qcompress = 0.6;
    videoEncodeCodecCxt->qmin = 10;
    videoEncodeCodecCxt->qmax = 51;
    videoEncodeCodecCxt->codec_type = AVMEDIA_TYPE_VIDEO;
//    av_opt_set(videoEncodeCodecCxt->priv_data,"preset","slow",0);
//    av_opt_set(videoEncodeCodecCxt->priv_data,"tune","zerolatency",0);
    ret = avcodec_open2(videoEncodeCodecCxt,videoEncodeAVcodec,NULL);

    //outfile
    avformat_alloc_output_context2(&outFormatCxt,NULL,NULL,"out.mp4");
    outVideoStream = avformat_new_stream(outFormatCxt,videoEncodeAVcodec);
    avcodec_parameters_from_context(outVideoStream->codecpar,videoEncodeCodecCxt);
    outAudioStream = avformat_new_stream(outFormatCxt,audioEncodeAVcodec);
    avcodec_parameters_from_context(outAudioStream->codecpar,audioEncodeCodecCxt);


    //output information.
    av_dump_format(outFormatCxt,0,"out.mp4",1);
    ret = avio_open(&outFormatCxt->pb,"out.mp4",AVIO_FLAG_WRITE);
    if (ret < 0) {
        fprintf(stderr, "Could not open output file '%s'", "out.mp4");
    }
    ret = avformat_write_header(outFormatCxt,NULL);
    if (ret < 0) {
        fprintf(stderr, "Error occurred when opening output file\n");
    }


    //transform
    while(1){
        avFrame = av_frame_alloc();
        ret = av_read_frame(fileFormatCxt,&pkt);
        if (ret < 0 ){
            printf("Could not read packet: %s\n", av_err2str(ret));
            break;
        }

        if (pkt.stream_index == audio_index){
            av_packet_rescale_ts(&pkt,fileFormatCxt->streams[audio_index]->time_base,audioEncodeCodecCxt->time_base);
            ret = avcodec_send_packet(audioAVCodecCxt,&pkt);
            while(ret >=0){
                ret = avcodec_receive_frame(audioAVCodecCxt,avFrame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
                    break;
                }
                encoding(fileFormatCxt->streams[audio_index],outFormatCxt->streams[audio_index],outFormatCxt,audioEncodeCodecCxt,avFrame,&pkt,&encodePkt);
            }
        }else if(pkt.stream_index == video_index){
//            printf("this is video packet.\n");
//            av_packet_rescale_ts(&pkt,fileFormatCxt->streams[video_index]->time_base,outFormatCxt->streams[video_index]->time_base);
            av_packet_rescale_ts(&pkt,fileFormatCxt->streams[video_index]->time_base,videoEncodeCodecCxt->time_base);
            ret = avcodec_send_packet(videoAVCodecCxt,&pkt);
            while(ret >=0){
                ret = avcodec_receive_frame(videoAVCodecCxt,avFrame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
                    break;
                }
//                printf("video width:%d\n",avFrame->width);
                encoding(fileFormatCxt->streams[video_index],outFormatCxt->streams[video_index],outFormatCxt,videoEncodeCodecCxt,avFrame,&pkt,&encodePkt);
            }
        }
        av_packet_unref(&pkt);
        av_packet_unref(&encodePkt);
    }
    av_write_trailer(outFormatCxt);


}

void encoding(AVStream *inputStream,AVStream *outputStream,AVFormatContext *outFormatCxt,AVCodecContext *avEncodeCodecCxt,AVFrame *avFrame,AVPacket *pkt,AVPacket *avEncodePacket){
    int ret = 0;
    ret = avcodec_send_frame(avEncodeCodecCxt,avFrame);
    if (ret <0){
        printf("error:%s\n",av_err2str(ret));
    }
    while(ret>=0){

        ret = avcodec_receive_packet(avEncodeCodecCxt,avEncodePacket);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
            break;
        }
        if (pkt->stream_index ==0){
            printf("-------------------------------------\n");
            printf("decoded packet pts :%lld\n",pkt->pts);
            printf("decoded packet dts :%lld\n",pkt->dts);
            printf("encoded frame pts :%lld\n",avFrame->pts);
            printf("encoded frame pkt_dts :%lld\n",avFrame->pkt_dts);
            printf("encoded packet pts :%lld\n",avEncodePacket->pts);
            printf("encoded packet dts :%lld\n",avEncodePacket->dts);
            printf("-------------------------------------\n");
            printf("");
//            avEncodePacket->pts = pkt->pts;
//            avEncodePacket->dts = pkt->dts;
//            avEncodePacket->duration = pkt->duration;
//            ret = av_write_frame(outFormatCxt,avEncodePacket);
        }
//        avEncodePacket->pts = av_rescale_q_rnd(pkt->pts,inputStream->time_base,outputStream->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
//        avEncodePacket->dts = av_rescale_q_rnd(pkt->dts,inputStream->time_base,outputStream->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
//        avEncodePacket->duration = 0;

//        av_packet_rescale_ts(avEncodePacket,avEncodeCodecCxt->time_base,outputStream->time_base);

        ret = av_interleaved_write_frame(outFormatCxt,avEncodePacket);

        if (ret < 0) {
            fprintf(stderr, "Error muxing packet\n");
            printf("error:%s\n",av_err2str(ret));
            break;
        }
    }
}

